#include "xy_was.h"
#include <memory.h>
#include <fstream>
#include <sstream>

XY_Was::XY_Was()
{
	_frames = nullptr;
}

XY_Was::~XY_Was()
{
	Free();
}

bool XY_Was::Load(const unsigned char* pData, uint32_t size, bool useTGA, const std::vector<XY_Was::Wpal>* wpal)
{
	if (size < sizeof(WasHeader))return false;
	Free();
	memcpy(&_header, pData, sizeof(WasHeader));
	if (_header.flags != 'PS')return false;
	WPixel wpl[256];
	memcpy(wpl, pData + _header.siHeader + 4, sizeof(wpl));
	if (wpal && wpal->size()) {
		for (const auto& info : *wpal) {
			if (info.clrIndex != -1 && info.clrIndex < info.matirxNum) {
				const auto& clr1 = info.clrMatirx[info.clrIndex * 3];
				const auto& clr2 = info.clrMatirx[info.clrIndex * 3 + 1];
				const auto& clr3 = info.clrMatirx[info.clrIndex * 3 + 2];
				for (uint32_t i = info.start;i < info.end;++i) {
					auto& clr = wpl[i];
					uint16_t r2, g2, b2;
					r2 = clr.red;
					g2 = clr.green;
					b2 = clr.blue;
					uint16_t r5 = r2*clr1.red + g2*clr1.green + b2*clr1.blue;
					uint16_t g6 = r2*clr2.red + g2*clr2.green + b2*clr2.blue;
					uint16_t b5 = r2*clr3.red + g2*clr3.green + b2*clr3.blue;
					r5 >>= 8;g6 >>= 8;b5 >>= 8;
					if (r5 > 0x1F)r5 = 0x1F;
					if (g6 > 0x3F)g6 = 0x3F;
					if (b5 > 0x1F)b5 = 0x1F;
					clr.red = r5;
					clr.green = g6;
					clr.blue = b5;
				}
			}
		}
	}

	int nOffset = _header.numDir*_header.numFrame;
	uint32_t* pDataOffset = (uint32_t*)(pData + _header.siHeader + 4 + sizeof(WPixel) * 256);
	_frames = new WasAniFrame[nOffset];
	for (int i = 0;i < nOffset;++i) {
		unsigned char* ptr = (unsigned char*)(pData + pDataOffset[i] + _header.siHeader + 4);
		WasAniHeader aniHeader;
		memcpy(&aniHeader,ptr, sizeof(aniHeader));
		memcpy(&_frames[i].header, &aniHeader, sizeof(aniHeader));
		_frames[i].size = aniHeader.width*aniHeader.height * 4;
		if (useTGA) _frames[i].size += 18;
		_frames[i].clrTable = new unsigned char[_frames[i].size];
		ptr += sizeof(aniHeader);
		uint32_t* colOffset = (uint32_t*)ptr;
		ptr += sizeof(uint32_t)*aniHeader.height;
		unsigned char* pClrTable = _frames[i].clrTable;
		if (useTGA) {
			*(uint32_t*)pClrTable = 0x20000;pClrTable += sizeof(uint32_t);
			*(uint32_t*)pClrTable = 0;pClrTable += sizeof(uint32_t);
			*(uint32_t*)pClrTable = 0;pClrTable += sizeof(uint32_t);
			*(uint16_t*)pClrTable = aniHeader.width;pClrTable += sizeof(uint16_t);
			*(uint16_t*)pClrTable = aniHeader.height;pClrTable += sizeof(uint16_t);
			*(uint16_t*)pClrTable = 0x820;pClrTable += sizeof(uint16_t);
		}
		uint32_t row = 0;
		for (int y = aniHeader.height-1;y >= 0;--y) {
			ptr = (unsigned char*)(pData + pDataOffset[i] + _header.siHeader + 4 + colOffset[y]);
			unsigned char* pLine = ptr;
			for (int x = 0;x < aniHeader.width;) {
				uint8_t ckData = *(uint8_t*)ptr;++ptr;
				uint8_t flag = ckData >> 6;
				if (flag == 0) {
					if ((ckData & 0x3F) == 0) {
						if (x < aniHeader.width) {
							int length = (aniHeader.width - x) * 4;
							memset(pClrTable, 0, length);
							pClrTable += length;
						}
						break;
					}
					else if ((ckData & 0x20) == 0x20) {
						uint8_t walpha = ckData & 0x1F;
						uint8_t index= *(uint8_t*)ptr;++ptr;
						uint32_t clr = wpl[index].to8888(walpha << 3);
						*(uint32_t*)pClrTable = clr;
						pClrTable += sizeof(uint32_t);
						++x;
					}else{
						uint8_t walpha = *(uint8_t*)ptr;++ptr;
						uint8_t index = *(uint8_t*)ptr;++ptr;
						uint32_t clr = wpl[index].to8888(walpha << 3);
						for (int k = 0;k < (ckData & 0x1F) && x < aniHeader.width;++k) {
							*(uint32_t*)pClrTable = clr;
							pClrTable += sizeof(uint32_t);
							++x;
						}
					}
				}
				else if (flag == 1) {
					for (int k = 0;k < (ckData & 0x3F) && x < aniHeader.width;++k) {
						uint8_t index = *(uint8_t*)ptr;++ptr;
						*(uint32_t*)pClrTable = wpl[index].to8888(0xFF);
						pClrTable += sizeof(uint32_t);
						++x;
					}
					
				}else if(flag==2){
					uint8_t index = *(uint8_t*)ptr;++ptr;
					for (int k = 0;k < (ckData & 0x3F) && x < aniHeader.width;++k) {
						*(uint32_t*)pClrTable = wpl[index].to8888(0xFF);
						pClrTable += sizeof(uint32_t);
						++x;
					}
				}
				else if (flag == 3) {
					uint8_t length = ckData & 0x3F;
					if (x + length > aniHeader.width)length = aniHeader.width - x;
					memset(pClrTable, 0, length * sizeof(uint32_t));
					pClrTable += length * sizeof(uint32_t);
					x += length;
				}
			}
		}
	}
	return true;
}

std::vector<XY_Was::Wpal> XY_Was::parseWpal(const unsigned char* pData)
{
	std::vector<XY_Was::Wpal> wpal;
	if (!pData || *(uint32_t*)pData != 0x6C617077) return wpal;
	const auto* ptr = pData + sizeof(uint32_t);
	uint32_t num = *(uint32_t*)ptr;ptr += sizeof(uint32_t);
	wpal.resize(num);
	uint32_t start = *(uint32_t*)ptr;ptr += sizeof(uint32_t);
	for (uint32_t i = 0;i < num;++i) {
		wpal[i].start = start;
		start = *(uint32_t*)ptr;
		ptr += sizeof(uint32_t);
		wpal[i].end = start;
	}
	for (uint32_t i = 0;i < num;++i) {
		auto& info = wpal[i];
		info.clrIndex = -1;
		info.matirxNum = *(int32_t*)ptr;
		ptr += sizeof(uint32_t);
		info.clrMatirx.resize(info.matirxNum * 3);
		auto* p = info.clrMatirx.data();
		uint32_t si = sizeof(Wpal::colorMatirx) * 3 * info.matirxNum;
		memcpy(p, ptr, si);
		ptr += si;
	}
	return wpal;
}

void XY_Was::Free(void)
{
	if (_frames) {
		delete[] _frames;
		_frames = nullptr;
	}
}
